// 这个是 listenfd 只负责获取连接 , 获取普通fd , 然后给 fd 派发任务
// listen 也看成 IO , 所以 listen 就是读事件 , Input 

#pragma once
#include <iostream>
#include <memory>
#include "Epoller.hpp"
#include "Socket.hpp"
#include "Common.hpp"
#include "Connect.hpp"
#include "Channel.hpp"

using namespace SocketModule;


// 继承父类 -- listen 只负责读事件 
class Listener : public Connect
{
    static const int defaultport = 8080;

public:
    Listener(int port = defaultport)
        : _port(port), _listensock(std::make_unique<TcpSocket>())
    {
        //1.创建 tcp 
        _listensock->BuildTcpSocketMethod(_port);
        //2 . 为 listen 设置读事件 , 并且是 ET 模式 
        SetEvent(EPOLLIN | EPOLLET);
        // 3. ET 模式必须将 fd 设置为非阻塞
        SetNoBlock(_listensock->Fd());
    }
    ~Listener() {}

    // 实现虚方法

    // 处理读
    void Recver() override
    {
        // 能直接读吗 ?? 可以 , 因为这里一轮一定不阻塞 , 因为是 epoll 通知我的
        // ET 模式下必须是循环读取(保证数据要读完) + 非阻塞 (非阻塞在初始时就设置了)
        InetAddr client; 
        while(true)
        {
            int sockfd = _listensock->Accept(&client); // 返回值 : 文件描述符
            if(sockfd == ACCEPT_ERR)
                break;
            else if(sockfd == ACCEPT_CONTINUE)
                continue;
            else if(sockfd == ACCEPT_DONE)
                break;
            else 
            {
                //真正读到了数据 , 正式拿到了一个合法的普通 fd 
                // 接下来就是普通文件描述符要做的事情了 

                //1. 构建普通fd 
                std::shared_ptr<Connect> conn = std::make_shared<Channel>(sockfd , client);
                // 2. 为普通 fd 添加事件 
                conn->SetEvent(EPOLLIN|EPOLLET);
            }
        }
    }

    // 处理写 -- listen 不用 
    void Sender() override {}

    // 处理异常
    void Excepter() override
    {
    }

    int GetSockFd() override
    {
        return _listensock->Fd();
    }

private:
    int _port;
    std::unique_ptr<Socket> _listensock;
};